﻿using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;

using Cookbook.Common;
using Cookbook.Services.Core.Commanding;
using Cookbook.Services.Core.Commands;
using Cookbook.Services.Core.Persistency.NHibernate;

namespace Cookbook.Services.Core.Handlers
{
    public class CommandFormatter
    {
        public string Format(Command command)
        {
            string commandName = command.GetType().Name.Replace("Command", "");
            var builder = new StringBuilder(commandName + "(");

            AppendIdentity(builder, command);
            AppendProperties(builder, command);

            builder.Append(")");

            return builder.ToString();
        }

        private static void AppendIdentity(StringBuilder builder, Command command)
        {
            var versionedCommand = command as VersionedCommand;
            if (versionedCommand != null)
            {
                var attribute = command.GetType().FindAttribute<AppliesToAggregateRootAttribute>();
                string idName = (attribute != null)
                    ? attribute.AggregateRootType.Name + DatabaseConstants.IdentitySuffix
                    : DatabaseConstants.IdentityColumn;

                builder.AppendFormat("{0}={1}, Version={2}, ", idName, versionedCommand.Id, versionedCommand.Version);
            }
        }

        private static void AppendProperties(StringBuilder builder, Command command)
        {
            var propertyInfos = 
                from propertyInfo in command.GetType().GetProperties()
                select propertyInfo;

            builder.Append(string.Join(", ", propertyInfos.Select(pi => FormatProperty(pi, command))));
        }

        private static string FormatProperty(PropertyInfo propertyInfo, Command command)
        {
            object value = propertyInfo.GetValue(command, null);

            if ((value is IEnumerable) && !(value is string))
            {
                IEnumerable<object> enumerable = ((IEnumerable)value).Cast<object>();

                value = string.Join(", ", enumerable.Select(x => x.ToString()));
            }
            else if (ReferenceEquals(value, null))
            {
                value = "<null>";
            }

            return propertyInfo.Name + "=" + value;
        }
    }
}